package com.lixh.sdk.base.http

import com.lixh.sdk.base.util.log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.retryWhen
import okhttp3.ResponseBody

suspend fun <T> request(
    request: suspend () -> BaseResPose<T>,
    error: (Int, String?) -> Unit = { _, _ -> },
    success: (T?) -> Unit,
) {
    return coroutineScope {
        try {
            val result = request.invoke()
            if (result.success()) {
                success.invoke(result.data)
            } else {
                error.invoke(result.code, result.message)
            }
        } catch (ex: Exception) {
            error.invoke(411, ex.message)
            ex.printStackTrace()
        }
    }
}

suspend fun <T> syncRequest(
    request: suspend () -> BaseResPose<T>,
    error: (Int, String?) -> Unit = { _, _ -> },
): T? {
    return coroutineScope {
        var result: T? = null
        try {
            val resPose = request.invoke()
            result = resPose.data
        } catch (ex: Exception) {
            error.invoke(411, ex.message)
            ex.printStackTrace()
        }
        result
    }
}

suspend fun <T, R : BaseResPose<T>> requestRaw(
    request: suspend () -> R,
    error: (Int, String?) -> Unit = { _, _ -> },
    success: (T?) -> Unit,
) {
    return coroutineScope {
        try {
            val result = request.invoke()
            if (result.success()) {
                success.invoke(result.data)
            } else {
                error.invoke(result.code, result.message)
            }
        } catch (ex: Exception) {
            error.invoke(411, ex.message)
            ex.printStackTrace()
        }
    }
}

suspend fun <T, R : BaseResPose<T>> syncRequestRaw2(
    request: suspend () -> R,
    error: (Int, String?) -> Unit = { _, _ -> },
): R? {
    return coroutineScope {
        var result: R? = null
        try {
            result = request.invoke()
        } catch (ex: Exception) {
            error.invoke(411, ex.message)
            ex.printStackTrace()
        }
        result
    }
}

// 满足条件才返回 否则进行重试
suspend fun <T, S : ApiService> requestRetry(
    service: S,
    error: (Int, String?) -> Unit = { _, _ -> },
    retryWhen: (cause: Throwable, attempt: Long) -> Boolean = { _, _ ->
        false
    },
    retryWhenTime: Long = 1000,
    request: suspend S.() -> BaseResPose<T>,
): Flow<T?> {
    return flow {
        try {
            emit(request(service))
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
    }.flowOn(Dispatchers.IO)
        //onCompletion的优势在于他有一个可空的Throwable参数 通过判断该参数 可以知道
        .onCompletion { cause ->
            cause?.message?.log("requestRetry")
        }.map {
            if (!it.success()) {
                throw ApiException(it.code, it.message ?: "")
            }
            it.data
        }.retryWhen { cause, attempt ->
            "retryWhen ${cause.message}".log("requestRetry")
            delay(retryWhenTime)
            retryWhen(cause, attempt)
        }.catch { cause ->
            "catch ${cause.message}".log("requestRetry")
            error.invoke(411, cause.message)
        }

}


suspend fun <T> Flow<T>.next(bloc: suspend T.() -> Unit): Unit =
    collect { bloc(it) }